package producersConsumersTests.meanInsertionTime;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import prod_cons.TaskContainer;
import producersConsumersTests.utils.DummyTask;
import producersConsumersTests.utils.DummyTaskFactory;
import producersConsumersTests.utils.Inserter;
import producersConsumersTests.utils.Remover;
import producersConsumersTests.utils.Test;
import producersConsumersTests.utils.Tester;
import producersConsumersTests.utils.TesterVisitor;

/**
 * Test #3 - Mean insertion time test This test measures average container
 * insertion time, using a MeanMeasureInserter. The MeanMeasureInserter just
 * performs nanoTime before and after the insertion, and adds that to a
 * measurements lists.
 */
public class MeanInsertionTimeTest implements Test {
	private static class MeanMeasureInserter extends Inserter<DummyTask> {
		private long start;
		private Collection<Long> measures = new LinkedList<Long>();
		private final int insertsBetweenMeasures;
		private int insertionNum = 0;

		public MeanMeasureInserter(TaskContainer container, int peakLength,
				int timeBetweenPeaks, int insertsBetweenMeasures) {
			super(container, peakLength, timeBetweenPeaks,
					new DummyTaskFactory());
			this.insertsBetweenMeasures = insertsBetweenMeasures;
		}

		public MeanMeasureInserter(TaskContainer container, double lambda,
				int insertsBetweenMeasures) {
			super(container, lambda, new DummyTaskFactory());
			this.insertsBetweenMeasures = insertsBetweenMeasures;
		}

		@Override
		protected void afterInsert(DummyTask task) {
			if (++insertionNum == insertsBetweenMeasures) {
				long finish = System.nanoTime();
				measures.add(finish - start);
				start = finish;
				insertionNum = 0;
			}
		}

		@Override
		protected void beforeBurst() {
			start = System.nanoTime();
		}

		@Override
		protected void beforeRun() {
			this.insertionNum = 0;
		}

		protected Collection<Long> getMeasures() {
			return measures;
		}
	}

	private static class MeanInsertionTimeTester extends
			Tester<MeanMeasureInserter, Remover> {
		private int insertionsBetweenMeasures;

		public MeanInsertionTimeTester(MeanMeasureInserter[] inserters,
				Remover[] removers, int timeToRun, TaskContainer container, int insertionsBetweenMeasures) {
			super(inserters, removers, timeToRun, container);
			this.insertionsBetweenMeasures = insertionsBetweenMeasures;
		}

		private List<Long> measurements = new LinkedList<Long>();
		private double average;;

		@Override
		protected void afterRun() {
			double sum = 0;

			for (MeanMeasureInserter inserter : inserters) {
				measurements.addAll(inserter.getMeasures());
				for (long measure : inserter.getMeasures())
					sum += ((double) measure) / 1000000 / insertionsBetweenMeasures;
			}
			average = sum / measurements.size();
			System.out.println("Mean time for insertion = " + average + " ms");
		}

		public List<Long> getResults() {
			return measurements;
		}

		@Override
		protected void beforeRun() {

		}
		
		public double getAverage() {
			return average;
		}
	}

	private int insertsBetweenMeasures;

	public MeanInsertionTimeTest() {
	}

	MeanInsertionTimeTester tester;

	@Override
	public void test(int insertersNum, int removersNum, int peakLength,
			int timeBetweenPeaks, int timeToRun, TaskContainer container,
			int extraInt) throws InterruptedException, InstantiationException,
			IllegalAccessException, ClassNotFoundException {
		insertsBetweenMeasures = extraInt;
		MeanMeasureInserter inserters[] = new MeanMeasureInserter[insertersNum];
		for (int i = 0; i < inserters.length; i++)
			inserters[i] = new MeanMeasureInserter(container, peakLength,
					timeBetweenPeaks, this.insertsBetweenMeasures);
		Remover removers[] = new Remover[removersNum];
		for (int i = 0; i < removers.length; i++)
			removers[i] = new Remover(container);
		tester = new MeanInsertionTimeTester(inserters, removers, timeToRun,
				container, extraInt);
		tester.runTest();
	}

	@Override
	public void test(int insertersNum, int removersNum, double lambda,
			int timeToRun, TaskContainer container, int extraInt)
			throws InterruptedException, InstantiationException,
			IllegalAccessException, ClassNotFoundException {
		insertsBetweenMeasures = extraInt;
		MeanMeasureInserter inserters[] = new MeanMeasureInserter[insertersNum];
		for (int i = 0; i < inserters.length; i++)
			inserters[i] = new MeanMeasureInserter(container, lambda,
					this.insertsBetweenMeasures);
		Remover removers[] = new Remover[removersNum];
		for (int i = 0; i < removers.length; i++)
			removers[i] = new Remover(container);
		tester = new MeanInsertionTimeTester(inserters, removers, timeToRun,
				container, extraInt);
		tester.runTest();
	}

	public List<Long> getResults() {
		return tester.getResults();
	}

	@Override
	public void accept(TesterVisitor v) {
		v.visit(this);
	}
	
	public int getInsertsBetweenMeasures() {
		return insertsBetweenMeasures;
	}
	
	public double getAverage() {
		return tester.getAverage();
	}
}
